﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace JasLib.Interop.Win32
{
    /// <summary>
    /// Pointer-sized unsigned integer.
    /// </summary>
    /// <remarks>
    /// This is a perfectly capable substitute for the ULONG_PTR, WPARAM, and SIZE_T Win32 types,
    /// and for the completely underwhelming System.UIntPtr framework type.
    /// </remarks>
    [StructLayout(LayoutKind.Explicit)]
    public struct UINT_PTR
    {
        public static readonly UINT_PTR Zero = new UINT_PTR(0);
        public static readonly int Size = MarshalHelper.SizeOf<UINT_PTR>();
        public static readonly int Bits = Size * 8;
        public static readonly int HighestBitIndex = Bits - 1;

        static UINT_PTR()
        {
            Debug.Assert(Size == UIntPtr.Size, "UINT_PTR absolutely MUST be the same size as a pointer.");
            Debug.Assert(Size <= 8, "UINT_PTR isn't supported for pointer sizes larger than 64 bits.");
            return;
        }

        [FieldOffset(0)]
        internal unsafe void* _value;

        public unsafe UINT_PTR(void* value)
        {
            _value = value;
        }

        public UINT_PTR(uint iValue)
        {
            unsafe
            {
                _value = (void*)iValue;
            }
        }

        public UINT_PTR(ulong lValue)
        {
            unsafe
            {
                _value = (void*)lValue;
            }
        }

        public override int GetHashCode()
        {
            unsafe
            {
                return (int)((ulong)this._value);
            }
        }

        public override bool Equals(object obj)
        {
            unsafe
            {
                return (obj is UINT_PTR) && ((UINT_PTR)obj)._value == _value;
            }
        }

        public override string ToString()
        {
            unsafe
            {
                return ((ulong)_value).ToString();
            }
        }

        public string ToString(string strFormat)
        {
            unsafe
            {
                return ((ulong)_value).ToString(strFormat);
            }
        }

        /// <summary>
        /// Bitfield access.
        /// </summary>
        /// <param name="iLeastSignificantBitIndex"></param>
        /// <param name="iBitWidth"></param>
        /// <returns></returns>
        public UINT_PTR this[int iLeastSignificantBitIndex, int iBitWidth]
        {
            get
            {
                unsafe
                {
                    Debug.Assert((iLeastSignificantBitIndex + iBitWidth) <= Bits);

                    ulong ulMask = 0;
                    ulong ulNextBit = ((ulong)1 << iLeastSignificantBitIndex);
                    for (uint uiIndex = 0; uiIndex < iBitWidth; uiIndex++)
                    {
                        ulMask |= ulNextBit;
                        ulNextBit <<= 1;
                    }

                    ulong ulValue = ((ulong)_value & ulMask);
                    ulValue >>= iLeastSignificantBitIndex;

                    return ulValue;
                }
            }
            set
            {
                unsafe
                {
                    Debug.Assert((iLeastSignificantBitIndex + iBitWidth) <= Bits);

                    ulong ulMask = 0;
                    ulong ulNextBit = ((ulong)1 << iLeastSignificantBitIndex);
                    for (int iIndex = 0; iIndex < iBitWidth; iIndex++)
                    {
                        ulMask |= ulNextBit;
                        ulNextBit <<= 1;
                    }

                    ulong ulNewValue = ((ulong)value._value << iLeastSignificantBitIndex) & ulMask;
                    _value = (void*)(((ulong)_value & ~ulMask) | ulNewValue);

                    return;
                }
            }
        }

        public UINT_PTR ReverseBits()
        {
            unsafe
            {
                ulong ulSource = (ulong)_value;
                ulong ulValue = 0;
                for (int iIndex = 0; iIndex < Bits; iIndex++)
                {
                    ulValue |= ((ulSource >> iIndex) & 1ul) << (HighestBitIndex - iIndex);
                }
                return ulValue;
            }
        }

        #region Typecast operators TO this type

        public unsafe static implicit operator UINT_PTR(void* value)
        {
            return new UINT_PTR { _value = value };
        }

        public static implicit operator UINT_PTR(int value)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)value };
            }
        }

        public static implicit operator UINT_PTR(uint value)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)value };
            }
        }

        public static implicit operator UINT_PTR(long value)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)value };
            }
        }

        public static implicit operator UINT_PTR(ulong value)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)value };
            }
        }

        public static implicit operator UINT_PTR(UIntPtr iptrOperand)
        {
            unsafe
            {
                return new UINT_PTR { _value = iptrOperand.ToPointer() };
            }
        }

        public static implicit operator UINT_PTR(IntPtr op1)
        {
            unsafe
            {
                return new UINT_PTR { _value = op1.ToPointer() };
            }
        }

        public static implicit operator UINT_PTR(SafeHandle op1)
        {
            unsafe
            {
                return new UINT_PTR { _value = op1.DangerousGetHandle().ToPointer() };
            }
        }

        #endregion

        #region Typecast operators FROM this type

        public unsafe static implicit operator void*(UINT_PTR op1)
        {
            return op1._value;
        }

        public static implicit operator INT_PTR(UINT_PTR op1)
        {
            unsafe
            {
                return op1._value;
            }
        }

        public static implicit operator UIntPtr(UINT_PTR op1)
        {
            unsafe
            {
                return new UIntPtr(op1._value);
            }
        }

        public static implicit operator IntPtr(UINT_PTR op1)
        {
            unsafe
            {
                return new IntPtr(op1._value);
            }
        }

        public static implicit operator ushort(UINT_PTR op1)
        {
            unsafe
            {
                return (ushort)op1._value;
            }
        }

        public static implicit operator uint(UINT_PTR op1)
        {
            unsafe
            {
                return (uint)op1._value;
            }
        }

        #endregion

        #region Arithmetic Operators

        public static UINT_PTR operator +(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value + (ulong)op2._value) };
            }
        }

        public static UINT_PTR operator ++(UINT_PTR op1)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value + 1L) };
            }
        }

        public static UINT_PTR operator -(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value - (ulong)op2._value) };
            }
        }

        public static UINT_PTR operator --(UINT_PTR op1)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value - 1L) };
            }
        }

        public static UINT_PTR operator *(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value * (ulong)op2._value) };
            }
        }

        public static UINT_PTR operator /(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value / (ulong)op2._value) };
            }
        }

        public static UINT_PTR operator %(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value % (ulong)op2._value) };
            }
        }

        #endregion

        #region Bitwise Operators

        public static UINT_PTR operator ~(UINT_PTR op1)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)~((ulong)op1._value) };
            }
        }

        public static UINT_PTR operator &(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value & (ulong)op2._value) };
            }
        }

        public static UINT_PTR operator |(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value & (ulong)op2._value) };
            }
        }

        public static UINT_PTR operator ^(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value ^ (ulong)op2._value) };
            }
        }

        public static UINT_PTR operator >>(UINT_PTR op1, int iBitCount)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value >> iBitCount) };
            }
        }

        public static UINT_PTR operator <<(UINT_PTR op1, int iBitCount)
        {
            unsafe
            {
                return new UINT_PTR { _value = (void*)((ulong)op1._value << iBitCount) };
            }
        }

        #endregion

        #region Logical Operators

        public static bool operator >(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return (ulong)op1._value > (ulong)op2._value;
            }
        }

        public static bool operator >=(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return (ulong)op1._value >= (ulong)op2._value;
            }
        }

        public static bool operator <(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return (ulong)op1._value < (ulong)op2._value;
            }
        }

        public static bool operator <=(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return (ulong)op1._value <= (ulong)op2._value;
            }
        }

        public static bool operator ==(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return op1._value == op2._value;
            }
        }

        public static bool operator !=(UINT_PTR op1, UINT_PTR op2)
        {
            unsafe
            {
                return op1._value != op2._value;
            }
        }

        #endregion
    }
}
